class AsyncSeriesWaterfallHook {
  constructor () {
    this.tasks = []
  }

  tap (name, task) {
    this.tasks.push(task)
  }

  tapAsync (name, task) {
    this.tasks.push(task)
  }

  callAsync (...args) {
    const finalCallback = args.pop()
    let index = 0
    const next = (data) => {
      const task = this.tasks[index]

      if (!task) {
        finalCallback(data)
        return
      }

      if (index === 0) {
        task(...args, next)
      } else {
        task(data, next)
      }
      index++
    }

    next(...args)
  }
}

let hook = new AsyncSeriesWaterfallHook(['name'])

hook.tapAsync('react', (data, cb) => {
  console.log('react execute => ', data)

  setTimeout(() => {
    console.log('react finish', 'save arg: => ', 'firstArguments')

    cb('firstArguments')
  }, 1000)
})

hook.tapAsync('node', (data, cb) => {
  console.log('node execute: => ', data)

  setTimeout(() => {
    console.log('node finish', 'save arg: => ', 'secondArguments')

    cb('secondArguments')
  }, 1000)
})

hook.callAsync('xyf', (data) => {
  console.log('end', data)
})
